import _ from 'lodash'
import { readdir } from 'node:fs/promises'
import { join } from 'node:path'

export class ServiceContainer {
  constructor() {
    this.services = new Map()
    this.instances = new Map()
    this.starting = new Map()
  }
  
  register(name, definition) {
    if (this.services.has(name)) {
      throw new Error(`Service ${name} already registered`)
    }
    
    this.services.set(name, {
      name,
      factory: definition.factory || definition,
      dependencies: definition.dependencies || [],
      singleton: definition.singleton !== false,
      routes: definition.routes || null,
      websocket: definition.websocket || null
    })
  }
  
  async get(name) {
    if (this.instances.has(name)) {
      return this.instances.get(name)
    }
    
    if (this.starting.has(name)) {
      throw new Error(`Circular dependency detected: ${name}`)
    }
    
    const definition = this.services.get(name)
    if (!definition) {
      throw new Error(`Service ${name} not found`)
    }
    
    try {
      this.starting.set(name, true)
      
      const deps = await this.resolveDependencies(definition.dependencies)
      
      const instance = await definition.factory(...deps)
      
      if (definition.singleton) {
        this.instances.set(name, instance)
      }
      
      this.starting.delete(name)
      return instance
      
    } catch (err) {
      this.starting.delete(name)
      throw new Error(`Failed to create service ${name}: ${err.message}`)
    }
  }
  
  async resolveDependencies(dependencies) {
    return Promise.all(
      _(dependencies).map(dep => {
        if (typeof dep === 'string') {
          return this.get(dep)
        }
        return dep
      }).value()
    )
  }
  
  getAllDefinitions() {
    return Array.from(this.services.values())
  }
  
  async bootstrap(app) {
    const definitions = this.getAllDefinitions()
    
    const sorted = this.topologicalSort(definitions)
    
    for (const name of sorted) {
      const def = this.services.get(name)
      if (def && def.singleton) {
        const instance = await this.get(name)
        
        // 注入 app 实例，让服务可以访问核心属性
        instance.app = app
        
        if (instance.onInit) {
          await instance.onInit()
        }
        
        if (instance.registerRoutes) {
          const routes = instance.registerRoutes(app.app)  // app.app 是 Hono 实例
          if (routes) {
            app.logger.info('ServiceContainer', `✅ Routes registered for ${name}`)
          }
        }
        
        if (instance.registerEventListeners) {
          instance.registerEventListeners(app.eventBus)
        }
        
        if (instance.registerScheduledTasks) {
          instance.registerScheduledTasks(app.scheduler)
        }
        
        if (instance.onStart) {
          await instance.onStart()
        }
      }
    }
  }
  
  async loadServices(dir = './services/modules') {
    try {
      const files = await readdir(dir)
      
      for (const file of files) {
        if (file.endsWith('.js')) {
          const modulePath = `../../${dir}/${file}`
          const module = await import(modulePath)
          const ServiceClass = module.default || module[Object.keys(module)[0]]
          
          if (ServiceClass && ServiceClass.metadata) {
            this.registerClass(ServiceClass)
          }
        }
      }
    } catch (err) {
      if (err.code !== 'ENOENT') {
        console.error(`Error loading services from ${dir}:`, err.message)
      }
    }
  }
  
  registerClass(ServiceClass) {
    const { name, dependencies = [], singleton = true } = ServiceClass.metadata
    
    this.register(name, {
      factory: async (...deps) => new ServiceClass(...deps),
      dependencies,
      singleton
    })
  }
  
  topologicalSort(definitions) {
    const visited = new Set()
    const result = []
    const defMap = new Map()
    
    // 建立名称到定义的映射
    definitions.forEach(def => {
      defMap.set(def.name, def)
    })
    
    const visit = (name) => {
      if (visited.has(name)) return
      visited.add(name)
      
      const def = defMap.get(name) || this.services.get(name)
      if (def && def.dependencies) {
        def.dependencies.forEach(dep => {
          if (typeof dep === 'string') visit(dep)
        })
      }
      
      result.push(name)
    }
    
    definitions.forEach(def => visit(def.name))
    return result
  }
}